using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using MacomberMap.Common.Types;
using System.Xml;
using System.Data.Common;
using MacomberMap.Common.Internals;
using MacomberMap.Common.User_Interfaces.Components;
using MacomberMap.Common.User_Interfaces.Network_Map.Components;
using MacomberMap.Common.Logging;
using MacomberMap.Common.Events;
using System.Windows.Forms;

namespace MacomberMap.Common.Components
{
    /// <summary>
    /// This class holds information on our substation
    /// </summary>
    [MM_Serializable_Target( MM_Serializable_Target.enumTargetType.Both)]
    public class MM_Substation : MM_Element
    {
        #region Variable interfaces
        /// <summary>
        /// Return a string of element types within the substation
        /// </summary>
        public String ElementTypeList
        {
            get
            {
                //TODO: Update permissions here
                String[] InList = new string[ElemTypes.Count];
                for (int a=0; a < ElemTypes.Count; a++)
                    InList[a] = ElemTypes[a].Name;                    
                Array.Sort(InList);
                return String.Join(", ", InList);
            }
        }

        /// <summary>
        /// Return a string of KV levels within the substation
        /// </summary>
        public String KVLevelList
        {
            get
            {
                //TODO: Update permissions here
                StringBuilder outStr = new StringBuilder();
                for (int a = 0; a < KVLevels.Count; a++)
                    if (KVLevels[a].Permitted)
                        outStr.Append((outStr.Length == 0 ? "" : ", ") + KVLevels[a].Name.Split(' ')[0]);
                return outStr.ToString();
            }
        }

        /// <summary>
        /// Return a string of KV levels (including 'KV') within the substation
        /// </summary>
        public String KVLevelFullList
        {
            get
            {
                StringBuilder outStr = new StringBuilder();
                for (int a = 0; a < KVLevels.Count; a++)
                    outStr.Append((a == 0 ? "" : ", ") + KVLevels[a].Name);
                return outStr.ToString();
            }
        }

        /// <summary>
        /// Get or set the latutude of the substation
        /// </summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public float Latitude
        {
            get { return LatLong.Y; }
            set { LatLong.Y = value; }
        }

        /// <summary>
        /// Get or set the longitude of the substation
        /// </summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public float Longitude
        {
            get { return LatLong.X; }
            set { LatLong.X = value; }
        }   

        #endregion

        #region Variable declarations
        /// <summary>The substation's long name</summary>        
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public String LongName;

        /// <summary>The Latitude and Longitude of the substation</summary>
        public PointF LatLong = new PointF(float.NaN, float.NaN);

        /// <summary>Units within this substation</summary>
        public MM_Element[] Units;

        /// <summary>Loads within this substation</summary>
        public MM_Element[] Loads;

        /// <summary>Transofrmers within this substation</summary>
        public MM_Element[] Transformers;

        /// <summary>Electrical buses within this substation</summary>
        public MM_Element[] ElectricalBuses;

        /// <summary>Capacitors and reactors within this substation</summary>
        public MM_Element[] ShuntCompensators;

        /// <summary>The substation's weather zone</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public MM_Zone WeatherZone;

        /// <summary>The substation's load zone</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public MM_Zone LoadZone;

        /// <summary>The collection of KV levels found within the substation</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public List<MM_KVLevel> KVLevels = new List<MM_KVLevel>(4);

        /// <summary>The collection of element types within the substation</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public List<MM_Element_Type> ElemTypes = new List<MM_Element_Type>(5);

        /// <summary>Override the substation so it's not serialized</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.None)]
        public new MM_Substation Substation = null;

        

        /// <summary>Override the KV Level so it's not serialized</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.None)]
        public new MM_KVLevel KVLevel = null;
        #endregion

        #region Initialization

        /// <summary>
        /// Create a new substation from XML
        /// </summary>
        /// <param name="BaseElement">Our base XML element</param>
        public MM_Substation(XmlElement BaseElement): base(BaseElement)
        {
            this.Name = MM_Repository.TitleCase(BaseElement.Attributes["Name"].Value);
            if (BaseElement.HasAttribute("LongName"))
                this.LongName = MM_Repository.TitleCase(BaseElement.Attributes["LongName"].Value);
            if (BaseElement.HasAttribute("LatLong"))
            {
                string[] inLatLong = BaseElement.Attributes["LatLong"].Value.Split(',');
                this.LatLong = new PointF(MM_Repository.ToSingle(inLatLong[0]), MM_Repository.ToSingle(inLatLong[1]));
            }
            this.TEID = UInt64.Parse(BaseElement.Attributes["TEID"].Value);
            MM_Repository.Substations.Add(this.Name, this);
            MM_Repository.TEIDs.Add(this.TEID, this);
            this.ElemType = MM_Overall_Display.ElemTypes["Substation"];
        }

        /// <summary>
        /// Create a new substation from a database reader
        /// </summary>
        /// <param name="DataReader">The database row reader</param>
        public MM_Substation(DbDataReader DataReader): base(DataReader)
        {
            this.ElemType = MM_Overall_Display.FindElementType("Substation");
        }

        /// <summary>
        /// Initialize a blank substation
        /// </summary>
        public MM_Substation()
        {
            this.ElemType = MM_Overall_Display.FindElementType("Substation");
        }



        #endregion

        /// <summary>
        /// Return the name of our substation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return GetType().Name.Substring(GetType().Name.LastIndexOf('_') + 1) + " " + this.LongName+ " (" + this.Name + " / " + this.TEID.ToString("#,##0") + ")";
        }
        /// <summary>
        /// Return the angle between this substation and another, in radians
        /// </summary>
        /// <param name="Substation">The other substation</param>
        /// <returns>Angle, in radians</returns>
        public float AngleTo(MM_Substation Substation)
        {
            float DeltaX = Substation.LatLong.X - LatLong.X;
            float DeltaY = Substation.LatLong.Y - LatLong.Y;
            return (float)Math.Atan2(DeltaY, DeltaX);
        }


        /// <summary>
        /// Return the distance between this substation and another, in miles
        /// </summary>
        /// <param name="Substation">The other substation</param>
        /// <returns>Distance, in miles.</returns>
        public float DistanceTo(MM_Substation Substation)
        {
            float R = 3963.1f; //Radius of the earth in miles
            float deltaLat = (LatLong.Y - Substation.LatLong.Y) * (float)Math.PI / 180f;
            float deltaLon = (LatLong.X - Substation.LatLong.X) * (float)Math.PI / 180f;
            float a = (float)Math.Sin(deltaLat / 2f) * (float)Math.Sin(deltaLat / 2f) + (float)Math.Cos(Substation.LatLong.Y * (float)Math.PI / 180f) * (float)Math.Cos(LatLong.Y * Math.PI / 180d) * (float)Math.Sin(deltaLon / 2f) * (float)Math.Sin(deltaLon / 2f);
            float c = 2f * (float)Math.Atan2(Math.Sqrt(a), Math.Sqrt(1f - a));
            return R * c;
        }

        /// <summary>
        /// Return a list of lines connected to this substation
        /// </summary>
        public MM_Line[] ConnectedLines
        {
            get
            {
                List<MM_Line> Lines = new List<MM_Line>();
                foreach (MM_Line LineToCheck in MM_Repository.Lines.Values)
                    if (LineToCheck.Substation1 == this || LineToCheck.Substation2 == this)
                        Lines.Add(LineToCheck);
                return Lines.ToArray();
            }
        }

        /// <summary>
        /// Return the display name of the substation (Name or Long name, depending on the preference
        /// </summary>
        public String DisplayName
        {
            get
            {
                if (MM_Overall_Display.UseLongNames)
                    return this.LongName;
                else
                    return this.Name;
            }
        }

        /// <summary>
        /// Determine the county in which the substation resides
        /// </summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.TypeCollection)]
        public MM_Boundary County
        {
            get
            {
                if (_County != null)
                    return _County;
                else if (float.IsNaN(this.LatLong.X))
                    return null;
                else
                    foreach (MM_Boundary Bound in MM_Repository.Counties.Values)
                        if (Bound.HitTest(this.LatLong))
                            return (_County = Bound);
            return null;
            }
            set { _County = value; }
        }
        private MM_Boundary _County;

        #region Menu building
        /// <summary>
        /// Add substation-specific items to the popup menu.
        /// </summary>
        /// <param name="MenuStrip"></param>
        public void AddMenuItems(ContextMenuStrip MenuStrip)
        {
            MenuStrip.Items.Add("Substation " + (this as MM_Substation).LongName + " (" + this.Name + ")");
            MenuStrip.Items.Add("  County: " + County.Name);
            MenuStrip.Items.Add("  Elements: " + ElementTypeList);
            MenuStrip.Items.Add("  KV Levels: " + KVLevelList);

        }
        #endregion

    }

}

